//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ProcessLigandPDBContact.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>
#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace LigandPDBSpace {
  
  bool EqualLigand (TNode <String>* ligand, void* ref) {

    return (!strcmp(ligand->Info()->Out(), (char*)ref))?true:false;

  }
  
}  
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ProcessLigandPDBContact::ProcessLigandPDBContact () {
  
  this->namefileOut             = new String ();
  this->namefileOnlyLigands     = new String ();
  this->namefileLigandList      = new String ();
  this->locationPDBAtomDomain   = new String ();
  this->locationPDBHetAtmDomain = new String ();
  this->fastaProfile            = new ProcessCathFASTAProfile ();
  this->ligandsContainer        = new TListE <String> ();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ProcessLigandPDBContact::ProcessLigandPDBContact (const ProcessLigandPDBContact& processLigandPDBContact) {
  
  this->namefileOut             = new String (processLigandPDBContact.namefileOut);
  this->namefileOnlyLigands     = new String (processLigandPDBContact.namefileOnlyLigands);
  this->namefileLigandList      = new String (processLigandPDBContact.namefileLigandList);
  this->locationPDBAtomDomain   = new String (processLigandPDBContact.locationPDBAtomDomain);
  this->locationPDBHetAtmDomain = new String (processLigandPDBContact.locationPDBHetAtmDomain);
  this->fastaProfile            = new ProcessCathFASTAProfile (processLigandPDBContact.fastaProfile);
  this->ligandsContainer        = new TListE <String> (processLigandPDBContact.ligandsContainer);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ProcessLigandPDBContact::ProcessLigandPDBContact (const ProcessLigandPDBContact* processLigandPDBContact) {
  
  this->namefileOut             = new String (processLigandPDBContact->namefileOut);
  this->namefileOnlyLigands     = new String (processLigandPDBContact->namefileOnlyLigands);
  this->namefileLigandList      = new String (processLigandPDBContact->namefileLigandList);
  this->locationPDBAtomDomain   = new String (processLigandPDBContact->locationPDBAtomDomain);
  this->locationPDBHetAtmDomain = new String (processLigandPDBContact->locationPDBHetAtmDomain);
  this->fastaProfile            = new ProcessCathFASTAProfile (processLigandPDBContact->fastaProfile);
  this->ligandsContainer        = new TListE <String> (processLigandPDBContact->ligandsContainer);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ProcessLigandPDBContact::~ProcessLigandPDBContact () {
  
  if (this->namefileOut)             delete this->namefileOut;
  if (this->namefileOnlyLigands)     delete this->namefileOnlyLigands;
  if (this->namefileLigandList)      delete this->namefileLigandList;
  if (this->locationPDBAtomDomain)   delete this->locationPDBAtomDomain;
  if (this->locationPDBHetAtmDomain) delete this->locationPDBHetAtmDomain;
  if (this->fastaProfile)            delete this->fastaProfile;
  if (this->ligandsContainer)        delete this->ligandsContainer;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::NamefileOut (String* namefileOut) {
  
  if (namefileOut) this->namefileOut->In(namefileOut);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::NamefileOnlyLigands (String* namefileOnlyLigands) {
  
  if (namefileOnlyLigands) this->namefileOnlyLigands->In(namefileOnlyLigands);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::NamefileLigandList (String* namefileLigandList) {
  
  if (namefileLigandList) this->namefileLigandList->In(namefileLigandList);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::LocationPDBAtomDomain (String* locationPDBAtomDomain) {
  
  if (locationPDBAtomDomain) this->locationPDBAtomDomain->In(locationPDBAtomDomain);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::LocationPDBHetAtmDomain (String* locationPDBHetAtmDomain) {
  
  if (locationPDBHetAtmDomain) this->locationPDBHetAtmDomain->In(locationPDBHetAtmDomain);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::FASTAProfile (ProcessCathFASTAProfile* fastaProfile) {
  
  if (fastaProfile) *(this->fastaProfile) = *(fastaProfile);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ProcessLigandPDBContact::LigandsContainer (TListE <String>* ligandsContainer) {
  
  if (ligandsContainer) *(this->ligandsContainer) = *(ligandsContainer);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ProcessLigandPDBContact::NamefileOut (void) {
  
  return this->namefileOut;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ProcessLigandPDBContact::NamefileOnlyLigands (void) {
  
  return this->namefileOnlyLigands;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ProcessLigandPDBContact::NamefileLigandList (void) {
  
  return this->namefileLigandList;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ProcessLigandPDBContact::LocationPDBAtomDomain (void) {
  
  return this->locationPDBAtomDomain;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ProcessLigandPDBContact::LocationPDBHetAtmDomain (void) {
  
  return this->locationPDBHetAtmDomain;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ProcessCathFASTAProfile* ProcessLigandPDBContact::FASTAProfile (void) {
  
  return this->fastaProfile;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* ProcessLigandPDBContact::LigandsContainer (void) {
  
  return this->ligandsContainer;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ProcessLigandPDBContact::CreateDomainContactProfileByLigand (double ansgtronDistance) {
  
  if (!this->locationPDBAtomDomain   || !this->locationPDBAtomDomain->Length()   ||
      !this->locationPDBHetAtmDomain || !this->locationPDBHetAtmDomain->Length() ||
      !this->namefileOut             || !this->namefileOut->Length()             ||
      !this->namefileOnlyLigands     || !this->namefileOnlyLigands->Length()     ||
      !this->namefileLigandList      || !this->namefileLigandList->Length()      ||
      !this->fastaProfile            || !this->fastaProfile->NamefileIn())
    return false;
  
  this->fastaProfile->ListCathDomainProfile()->Clear();
  if (!this->fastaProfile->LoadData()) return false;

  bool isGZFileOut        = (this->namefileOut->Contain("gz"))?1:0;
  bool isGZFileLigandList = (this->namefileOnlyLigands->Contain("gz"))?1:0;

  fstream fileOut, fileOutOnlyLigands, fileLigandList;
  gzFile  zipFileOut, zipFileLigandList;

  if (isGZFileOut) zipFileOut = gzopen (this->namefileOut->Out(), "wb9");
  else             fileOut.open (this->namefileOut->Out(), fstream::out);

  if (isGZFileLigandList) zipFileLigandList = gzopen (this->namefileOnlyLigands->Out(), "wb9");
  else                    fileOutOnlyLigands.open (this->namefileOnlyLigands->Out(), fstream::out);

  fileLigandList.open(this->namefileLigandList->Out(), fstream::out);

  this->ligandsContainer->Clear();

  this->fastaProfile->ListCathDomainProfile()->SetInitial();
  while (this->fastaProfile->ListCathDomainProfile()->SetNext()) {

    LigandPDBContact* ligandPDBContact = new LigandPDBContact ();
    
    CathFASTAProfile* currentDomainProfile = this->fastaProfile->ListCathDomainProfile()->GetCurrent();

    //cout << currentDomainProfile->ToStringHeader()->Out() << endl;

    ligandPDBContact->FilenameDomainIn()->In(this->locationPDBAtomDomain); 
    ligandPDBContact->FilenameDomainIn()->Add("/");
    ligandPDBContact->FilenameDomainIn()->Add(currentDomainProfile->Domain());

    ligandPDBContact->ListLigands()->FilenameIn()->In(this->locationPDBHetAtmDomain); 
    ligandPDBContact->ListLigands()->FilenameIn()->Add("/");
    ligandPDBContact->ListLigands()->FilenameIn()->Add(currentDomainProfile->Domain());

    if (!ligandPDBContact->LoadData()) {
      cout << "Error Loading Data! Domain: " << currentDomainProfile->Domain()->Out() << endl;
      continue;
    }

    if (!ligandPDBContact->CheckForContacts(ansgtronDistance)) {
      cout << "Error checking contacts! Domain: " << currentDomainProfile->Domain()->Out() << endl;
      continue;
    }

    if (isGZFileOut) gzprintf(zipFileOut, "%s\n", currentDomainProfile->ToStringHeader()->Out());
    else             fileOut << currentDomainProfile->ToStringHeader()->Out() << endl;

    if (isGZFileLigandList) gzprintf(zipFileLigandList, "%s\n", currentDomainProfile->ToStringHeader()->Out());
    else                    fileOutOnlyLigands << currentDomainProfile->ToStringHeader()->Out() << endl;

    if (!ligandPDBContact->ListContactByLigands()->Length()) {
      
      if (isGZFileOut) gzprintf(zipFileOut, ",\n");
      else             fileOut << endl;
      
      if (isGZFileLigandList) gzprintf(zipFileLigandList, ",\n");
      else                    fileOutOnlyLigands << endl;
      
      if (ligandPDBContact) delete ligandPDBContact;
      
      continue;
      
    }

    ligandPDBContact->ListContactByLigands()->SetInitial();
    while (ligandPDBContact->ListContactByLigands()->SetNext()) {
      
      String* currentContactByLigand = ligandPDBContact->ListContactByLigands()->GetCurrent();
      
      if (isGZFileOut) gzprintf(zipFileOut, "%s\n", currentContactByLigand->Out());
      else             fileOut << currentContactByLigand->Out() << endl;
    
      char* part = strtok(currentContactByLigand->Out(), "-");
      if (part) {

        if (isGZFileLigandList) gzprintf(zipFileLigandList, "%s,", part);
        else                    fileOutOnlyLigands << "," << part;
        
        String* searchLigand = this->ligandsContainer->FirstOcurrence(LigandPDBSpace::EqualLigand, part);
        
        if (!searchLigand) this->ligandsContainer->Add(new String (part));
        
      }
      
    }

    if (isGZFileLigandList) gzprintf(zipFileLigandList, "\n");
    else                    fileOutOnlyLigands << endl;
    
    if (ligandPDBContact) delete ligandPDBContact;

  }

  if (isGZFileOut) gzclose(zipFileOut);
  else             fileOut.close();

  if (isGZFileLigandList) gzclose(zipFileLigandList);
  else                    fileOutOnlyLigands.close();

  this->ligandsContainer->SetInitial();
  while (this->ligandsContainer->SetNext()) 
    fileLigandList << this->ligandsContainer->GetCurrent()->Out() << endl;
  fileLigandList.close();

  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ProcessLigandPDBContact ProcessLigandPDBContact::operator= (const ProcessLigandPDBContact& processLigandPDBContact) {
  
  this->namefileOut->In(processLigandPDBContact.namefileOut);
  this->namefileOnlyLigands->In(processLigandPDBContact.namefileOnlyLigands);
  this->namefileLigandList->In(processLigandPDBContact.namefileLigandList);
  this->locationPDBAtomDomain->In(processLigandPDBContact.locationPDBAtomDomain);
  this->locationPDBHetAtmDomain->In(processLigandPDBContact.locationPDBHetAtmDomain);
  
  *(this->fastaProfile)     = *(processLigandPDBContact.fastaProfile);
  *(this->ligandsContainer) = *(processLigandPDBContact.ligandsContainer);
  
  return *this;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//

